-module(db_gui).

-include("db.hrl").

-ifdef(windowed).
-include_lib("wx/include/wx.hrl").

-export([start/0]).

-define(FIELD_NODE_COUNT, 101).
-define(STATUS_LIST, 102).
-define(TEXT_VISUAL_HOST, 103).

-define(BTN_UPDATE_NODE_COUNT, 111).
-define(BTN_START_ALL, 112).
-define(BTN_STOP_ALL, 113).
-define(BTN_INSERT, 114).
-define(BTN_UPDATE_STATUS, 115).
-define(BTN_START_SELECTED, 116).
-define(BTN_STOP_SELECTED, 117).

-define(RADIO_NETWORK, 131).
-define(RADIO_CHORD_SETUP, 132).
-define(RADIO_CHORD_LOOKUP, 133).
-define(RADIO_BACKEND, 134).
-define(RADIO_VERBOSE, 135).
-define(RADIO_USE_JOIN_DELAY, 136).
-define(RADIO_VISUAL, 137).

-define(SPIN_REDUNDANCY, 151).
-define(SPIN_HASHSIZE, 152).
-define(SPIN_NODE_AMOUNT, 153).
-define(SPIN_VALUE_COUNT, 154).
-define(SPIN_JOIN_DELAY, 155).

start() ->
    Wx = wx:new(),
    State = wx:batch(fun() -> create_window(Wx) end),
    Frame = element(1,State),
    wxWindow:show(Frame),
    loop(State),
    wx:destroy(),
    ok.

create_window(Wx) ->
    Frame = wxFrame:new(Wx, -1, "Database control", [{size, {1000,700}}]),
    Panel  = wxPanel:new(Frame),

    wxFrame:connect(Frame, close_window),

    MenuBar  = wxMenuBar:new(),
    FileM    = wxMenu:new([]),
    HelpM    = wxMenu:new([]),

    % unlike wxwidgets the stock menu items still need text to be given, 
    % although help text does appear
    _QuitMenuItem  = wxMenu:append(FileM, ?wxID_EXIT, "&Quit"),
    % Note the keybord accelerator
    _AboutMenuItem = wxMenu:append(HelpM, ?wxID_ABOUT, "&About...\tF1"),

    wxMenu:appendSeparator(HelpM),    
    ContentsMenuItem = wxMenu:append(HelpM, ?wxID_HELP_CONTENTS, "&Contents"),
    wxMenuItem:enable(ContentsMenuItem, [{enable, false}]),

    ok = wxFrame:connect(Frame, command_menu_selected), 

    wxMenuBar:append(MenuBar, FileM, "&File"),
    wxMenuBar:append(MenuBar, HelpM, "&Help"),
    wxFrame:setMenuBar(Frame, MenuBar),

    ok = wxFrame:setStatusText(Frame, "",[]),

    % Sizers
    SuperSizer = wxBoxSizer:new(?wxHORIZONTAL),
    MainSizer   = wxBoxSizer:new(?wxVERTICAL),
    StatusSizer = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label,"Status"}]),
    StatusButtonSizer = wxBoxSizer:new(?wxHORIZONTAL),
    NodeCountSizer  = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "#Erlang Nodes: "}]),
    RadioSizer   = wxBoxSizer:new(?wxHORIZONTAL),
    ChordModuleRadioSizer = wxBoxSizer:new(?wxHORIZONTAL),
    ChordIntervalSpinSizer = wxBoxSizer:new(?wxHORIZONTAL),
    JoinSettingsSizer = wxBoxSizer:new(?wxHORIZONTAL),
    VisualSizer = wxBoxSizer:new(?wxHORIZONTAL),
    SpinSizer = wxBoxSizer:new(?wxHORIZONTAL),
    RedundancySizer  = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Redundancy: "}]),
    HashsizeSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Hashsize: "}]),
    StabilizeIntervalSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Stabilize interval: "}]),
    FixFingersIntervalSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Fix fingers interval: "}]),
    JoinDelaySizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel,
                                            [{label, "Join delay: "}]),
    VisualHostSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel,
                                            [{label, "Visualization host: "}]),
    NodeAmountSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Select amount of nodes"}]),
    StartStopSizer = wxBoxSizer:new(?wxHORIZONTAL),
    InsertSizer = wxStaticBoxSizer:new(?wxHORIZONTAL, Panel, 
                                            [{label, "Insert random values"}]),

    % Buttons, field etc..
    FieldNodeCount = wxStaticText:new(Panel, ?FIELD_NODE_COUNT, 
                        integer_to_list(length(nodes())+1), [{size,{200,15}}]),
    BtnUpdateNodeCount = wxButton:new(Panel, ?BTN_UPDATE_NODE_COUNT, [{label, "&Update"}]),
    RadioNetwork = wxRadioBox:new(Panel, ?RADIO_NETWORK, "Network", ?wxDefaultPosition
                        ,?wxDefaultSize, ["mesh","chord"]),
    RadioBackend = wxRadioBox:new(Panel, ?RADIO_BACKEND, "Backend", ?wxDefaultPosition
                        ,?wxDefaultSize, ["ets","dict"]),
    RadioVerbose = wxRadioBox:new(Panel, ?RADIO_VERBOSE, "Verbose", ?wxDefaultPosition
                        ,?wxDefaultSize, ["true","false"]),
    RadioChordSetup = wxRadioBox:new(Panel, ?RADIO_CHORD_SETUP, "Chord setup module", ?wxDefaultPosition
                        ,?wxDefaultSize, ["aggressive","stabilized"]),
    RadioChordLookup = wxRadioBox:new(Panel, ?RADIO_CHORD_LOOKUP, "Chord lookup module", ?wxDefaultPosition
                        ,?wxDefaultSize, ["iterative","distributed"]),
    SpinRedundancy = wxSpinCtrl:new(Panel, [{min,0}, {max,20}, {initial,0}]),
    SpinHashsize = wxSpinCtrl:new(Panel, [{min,4}, {max,160}, {initial,160}]),
    SpinStabilizeInterval = wxSpinCtrl:new(Panel, [{min,1}, {max,1000000}, {initial,10000}]),
    SpinFixFingersInterval = wxSpinCtrl:new(Panel, [{min,1}, {max,1000000}, {initial,10000}]),
    RadioUseJoinDelay = wxRadioBox:new(Panel, ?RADIO_USE_JOIN_DELAY, "Use join delay", ?wxDefaultPosition
                         ,?wxDefaultSize, ["true", "false"]),
    SpinJoinDelay = wxSpinCtrl:new(Panel, [{min,0}, {max,1000000}, {initial,5000}]),
    RadioVisual = wxRadioBox:new(Panel, ?RADIO_VISUAL, "Visualization", ?wxDefaultPosition
                         ,?wxDefaultSize, ["on", "off"]),
    {ok, Hostname} = inet:gethostname(),
    TextVisualHost = wxTextCtrl:new(Panel, ?TEXT_VISUAL_HOST, [{value, Hostname}]),
    SpinNodeAmount = wxSpinCtrl:new(Panel, [{min,1}, {max,100000}, {initial,1}, {size,{200,25}}]),
    BtnStartAll  = wxButton:new(Panel, ?BTN_START_ALL, [{label, "&Start"}]),
    BtnStopAll  = wxButton:new(Panel, ?BTN_STOP_ALL, [{label, "&Stop"}]),

    SpinValueCount = wxSpinCtrl:new(Panel, [{min,1}, {max,1000000000}, {initial,1}, {size,{200,25}}]),
    BtnInsert = wxButton:new(Panel, ?BTN_INSERT, [{label, "&Insert"}]),
    wxButton:disable(BtnInsert),

    BtnUpdateStatus = wxButton:new(Panel, ?BTN_UPDATE_STATUS, [{label, "&Update"}]),
    BtnStartSelected = wxButton:new(Panel, ?BTN_START_SELECTED, [{label, "&Start Selected"}]),
    BtnStopSelected = wxButton:new(Panel, ?BTN_STOP_SELECTED, [{label, "&Stop Selected"}]),
    StatusList = wxTreeCtrl:new(Panel, [ {size, {500,600}},
          {style, ?wxTR_HIDE_ROOT bor ?wxTR_HAS_BUTTONS bor ?wxTR_MULTIPLE}]),

    % Insert items
    wxSizer:add(SuperSizer, MainSizer, []),
    wxSizer:addSpacer(SuperSizer, 15),
    wxSizer:add(SuperSizer, StatusSizer, []),
    
    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(NodeCountSizer, FieldNodeCount,[]),
    wxSizer:add(MainSizer, NodeCountSizer, []),

    wxSizer:add(MainSizer, BtnUpdateNodeCount,[]),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(RadioSizer, RadioNetwork, []),
    wxSizer:addSpacer(RadioSizer, 5),
    wxSizer:add(RadioSizer, RadioBackend, []),
    wxSizer:addSpacer(RadioSizer, 5),
    wxSizer:add(RadioSizer, RadioVerbose, []),
    wxSizer:add(MainSizer, RadioSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(ChordModuleRadioSizer, RadioChordSetup, []),
    wxSizer:addSpacer(ChordModuleRadioSizer, 5),
    wxSizer:add(ChordModuleRadioSizer, RadioChordLookup, []),
    wxSizer:add(MainSizer, ChordModuleRadioSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(RedundancySizer, SpinRedundancy, []),
    wxSizer:add(HashsizeSizer, SpinHashsize, []),
    wxSizer:add(SpinSizer, RedundancySizer, []),
    wxSizer:add(SpinSizer, HashsizeSizer, []),
    wxSizer:add(MainSizer, SpinSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(StabilizeIntervalSizer, SpinStabilizeInterval, []),
    wxSizer:add(FixFingersIntervalSizer, SpinFixFingersInterval, []),
    wxSizer:add(ChordIntervalSpinSizer, StabilizeIntervalSizer, []),
    wxSizer:addSpacer(ChordIntervalSpinSizer, 5),
    wxSizer:add(ChordIntervalSpinSizer, FixFingersIntervalSizer, []),
    wxSizer:add(MainSizer, ChordIntervalSpinSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(JoinDelaySizer, SpinJoinDelay, []),
    wxSizer:add(JoinSettingsSizer, RadioUseJoinDelay, []),
    wxSizer:addSpacer(JoinSettingsSizer, 5),
    wxSizer:add(JoinSettingsSizer, JoinDelaySizer, []),
    wxSizer:add(MainSizer, JoinSettingsSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(VisualHostSizer, TextVisualHost, []),
    wxSizer:add(VisualSizer, RadioVisual, []),
    wxSizer:addSpacer(VisualSizer, 5),
    wxSizer:add(VisualSizer, VisualHostSizer, []),
    wxSizer:add(MainSizer, VisualSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(NodeAmountSizer, SpinNodeAmount, []),
    wxSizer:add(MainSizer, NodeAmountSizer, []),

    wxSizer:addSpacer(MainSizer, 5),
    wxSizer:add(StartStopSizer, BtnStartAll,[]),
    wxSizer:addSpacer(StartStopSizer, 5),
    wxSizer:add(StartStopSizer, BtnStopAll,[]),
    wxSizer:add(MainSizer, StartStopSizer, []),

    wxSizer:addSpacer(MainSizer, 10),
    wxSizer:add(InsertSizer, SpinValueCount, []),
    wxSizer:add(InsertSizer, BtnInsert, []),
    wxSizer:add(MainSizer, InsertSizer,[]),
    
    wxSizer:addSpacer(StatusSizer, 5),
    wxSizer:add(StatusButtonSizer, BtnUpdateStatus,[]),
    wxSizer:addSpacer(StatusButtonSizer, 5),
    wxSizer:add(StatusButtonSizer, BtnStartSelected,[]),
    wxSizer:addSpacer(StatusButtonSizer, 5),
    wxSizer:add(StatusButtonSizer, BtnStopSelected,[]),
    wxSizer:add(StatusSizer, StatusButtonSizer, []),
    wxSizer:addSpacer(StatusSizer, 5),
    wxSizer:add(StatusSizer, StatusList,[]),

    wxPanel:connect(Panel, command_button_clicked),

    wxPanel:setSizer(Panel, SuperSizer),

    {Frame,FieldNodeCount,TextVisualHost,
     RadioNetwork,RadioBackend,RadioChordSetup,RadioChordLookup,RadioVerbose,RadioUseJoinDelay,RadioVisual,
     SpinRedundancy,SpinHashsize,SpinStabilizeInterval,SpinFixFingersInterval,SpinNodeAmount,SpinValueCount,SpinJoinDelay,
     StatusList,BtnStartAll,BtnInsert}.

loop(State) ->
    {Frame,FieldNodeCount,_TextVisualHost,
     _RadioNetwork,_RadioBackend,_RadioChordSetup,_RadioChordLookup,_RadioVerbose,_RadioUseJoinDelay,_RadioVisual,
     _SpinRedundancy,_SpinHashsize,_SpinStabilizeInterval,_SpinFixFingersInterval,SpinNodeAmount,SpinValueCount,_SpinJoinDelay,
     StatusList,_BtnStartAll,_BtnInsert} = State,
    receive 
    #wx{event=#wxClose{}} ->
  	    io:format("~p Closing window ~n",[self()]),
  	    wxFrame:destroy(Frame),
  	    ok;
    #wx{id=?wxID_EXIT, event=#wxCommand{type=command_menu_selected}} ->
	    wxWindow:destroy(Frame),
	    ok;
    #wx{id=?wxID_ABOUT, event=#wxCommand{type=command_menu_selected}} ->
	    dialog(?wxID_ABOUT, Frame),
	    loop(State);
    #wx{id = ?BTN_UPDATE_NODE_COUNT, event=#wxCommand{type = command_button_clicked} } ->
        wxStaticText:setLabel(FieldNodeCount, integer_to_list(length(nodes())+1)),
        loop(State);
    #wx{id = ?BTN_START_ALL, event=#wxCommand{type = command_button_clicked} } ->
        % Pressed Start button
        NodeAmount = wxSpinCtrl:getValue(SpinNodeAmount),
        {Env, UseJoinDelay, JoinDelay} = get_env_list(State),

        disable_options(State),
        master:start_random_args(NodeAmount, Env, list_to_atom(UseJoinDelay), JoinDelay),
	    loop(State);
    #wx{id = ?BTN_STOP_ALL, event=#wxCommand{type = command_button_clicked} } ->
        % Pressed stop button
        master:stop_all(),
        enable_options(State),
	    loop(State);
    #wx{id = ?BTN_INSERT, event=#wxCommand{type = command_button_clicked} } ->
        Amount = wxSpinCtrl:getValue(SpinValueCount),
        Values = testing:generate_values(Amount-1),
        Node = master:get_random_db_node(),
        [rpc:call(Node, node, put, [Value, Value]) || Value <- Values],
	    loop(State);
    #wx{id = ?BTN_UPDATE_STATUS, event=#wxCommand{type = command_button_clicked} } ->
        wxTreeCtrl:deleteAllItems(StatusList),
        updateStatusList(StatusList),
	    loop(State);
    #wx{id = ?BTN_START_SELECTED, event=#wxCommand{type = command_button_clicked} } ->
        Nodes = parse_tree_selection(StatusList),
        {Env, UseJoinDelay, JoinDelay} = get_env_list(State),

        disable_options(State),
        master:start_selected_args(Nodes, Env, list_to_atom(UseJoinDelay), JoinDelay),
	    loop(State);
    #wx{id = ?BTN_STOP_SELECTED, event=#wxCommand{type = command_button_clicked} } ->
        Nodes = parse_tree_selection(StatusList),
        master:stop_selected(Nodes),
        loop(State);
    #wx{id = Id, event = Event} ->
        io:format("Got id: ~p, event: ~p~n", [Id, Event]),
        loop(State);
    Msg ->
	    io:format("Got message: ~p~n", [Msg]),
	    loop(State)
    after 1000 ->
	io:fwrite("."),
	loop(State)
    end.

dialog(?wxID_ABOUT,  Frame) ->
    Str = string:join(["About."], 
		      ""),
    MD = wxMessageDialog:new(Frame,
   			     Str,
   			     [{style, ?wxOK bor ?wxICON_INFORMATION}, 
   			      {caption, "About Database controller"}]),

    wxDialog:showModal(MD),
    wxDialog:destroy(MD).

get_env_list(State) ->
    {_Frame,_FieldNodeCount,TextVisualHost,
     RadioNetwork,RadioBackend,RadioChordSetup,RadioChordLookup,RadioVerbose,RadioUseJoinDelay,RadioVisual,
     SpinRedundancy,SpinHashsize,SpinStabilizeInterval,SpinFixFingersInterval,_SpinNodeAmount,_SpinValueCount,SpinJoinDelay,
     _StatusList,_BtnStartAll,_BtnInsert} = State,
        Network = wxRadioBox:getString(RadioNetwork
                    ,wxRadioBox:getSelection(RadioNetwork)),
        ChordSetup = wxRadioBox:getString(RadioChordSetup
                    ,wxRadioBox:getSelection(RadioChordSetup)),
        ChordLookup = wxRadioBox:getString(RadioChordLookup
                    ,wxRadioBox:getSelection(RadioChordLookup)),
        Backend = wxRadioBox:getString(RadioBackend
                    ,wxRadioBox:getSelection(RadioBackend)),
        Verbose = wxRadioBox:getString(RadioVerbose
                    ,wxRadioBox:getSelection(RadioVerbose)),
        Redundancy = wxSpinCtrl:getValue(SpinRedundancy),
        Hashsize = wxSpinCtrl:getValue(SpinHashsize),
        StabilizeInterval = wxSpinCtrl:getValue(SpinStabilizeInterval),
        FixFingersInterval = wxSpinCtrl:getValue(SpinFixFingersInterval),
        UseJoinDelay = wxRadioBox:getString(RadioUseJoinDelay
                     ,wxRadioBox:getSelection(RadioUseJoinDelay)),
        JoinDelay = wxSpinCtrl:getValue(SpinJoinDelay),
        Visual = wxRadioBox:getString(RadioVisual
                    ,wxRadioBox:getSelection(RadioVisual)),
        VisualHost = wxTextCtrl:getValue(TextVisualHost),
        VisualNode = case list_to_atom(Visual) of
            on -> list_to_atom("java@" ++ VisualHost);
            off -> undefined
        end,
    Env = [{network, list_to_atom(Network)},
     {chordsetup, list_to_atom(ChordSetup)},
     {chordlookup, list_to_atom(ChordLookup)},
     {backend, list_to_atom(Backend)},
     {verbose, list_to_atom(Verbose)},
     {redundancy, Redundancy},
     {hashsize, Hashsize},
     {stabilize_interval, StabilizeInterval},
     {fix_fingers_interval, FixFingersInterval},
     {visualNode, VisualNode}],
    {Env, UseJoinDelay, JoinDelay}.

enable_options(State) ->
    {_Frame,_FieldNodeCount,TextVisualHost,
     RadioNetwork,RadioBackend,RadioChordSetup,RadioChordLookup,RadioVerbose,RadioUseJoinDelay,RadioVisual,
     SpinRedundancy,SpinHashsize,SpinStabilizeInterval,SpinFixFingersInterval,SpinNodeAmount,SpinValueCount,SpinJoinDelay,
     _StatusList,BtnStartAll,BtnInsert} = State,
    wxRadioBox:enable(RadioNetwork),
    wxRadioBox:enable(RadioChordSetup),
    wxRadioBox:enable(RadioChordLookup),
    wxRadioBox:enable(RadioBackend),
    wxRadioBox:enable(RadioVerbose),
    wxRadioBox:enable(RadioUseJoinDelay),
    wxRadioBox:enable(RadioVisual),
    wxSpinCtrl:enable(SpinRedundancy),
    wxSpinCtrl:enable(SpinHashsize),
    wxSpinCtrl:enable(SpinStabilizeInterval),
    wxSpinCtrl:enable(SpinFixFingersInterval),
    wxSpinCtrl:enable(SpinJoinDelay),
    wxSpinCtrl:enable(SpinNodeAmount),
    wxTextCtrl:enable(TextVisualHost),
    wxButton:enable(BtnStartAll),
    wxSpinCtrl:disable(SpinValueCount),
    wxButton:disable(BtnInsert).

disable_options(State) ->
    {_Frame,_FieldNodeCount,TextVisualHost,
     RadioNetwork,RadioBackend,RadioChordSetup,RadioChordLookup,RadioVerbose,RadioUseJoinDelay,RadioVisual,
     SpinRedundancy,SpinHashsize,SpinStabilizeInterval,SpinFixFingersInterval,SpinNodeAmount,SpinValueCount,SpinJoinDelay,
     _StatusList,BtnStartAll,BtnInsert} = State,
    wxRadioBox:disable(RadioNetwork),
    wxRadioBox:disable(RadioChordSetup),
    wxRadioBox:disable(RadioChordLookup),
    wxRadioBox:disable(RadioBackend),
    wxRadioBox:disable(RadioVerbose),
    wxRadioBox:disable(RadioUseJoinDelay),
    wxRadioBox:disable(RadioVisual),
    wxSpinCtrl:disable(SpinRedundancy),
    wxSpinCtrl:disable(SpinHashsize),
    wxSpinCtrl:disable(SpinStabilizeInterval),
    wxSpinCtrl:disable(SpinFixFingersInterval),
    wxSpinCtrl:disable(SpinJoinDelay),
    wxSpinCtrl:disable(SpinNodeAmount),
    wxTextCtrl:disable(TextVisualHost),
    wxButton:disable(BtnStartAll),
    wxSpinCtrl:enable(SpinValueCount),
    wxButton:enable(BtnInsert).

updateStatusList(List) -> 
    Root = wxTreeCtrl:addRoot(List,"Erlang Nodes"),
    % Find all erlang nodes and add them to the root item
    All_Nodes = [node()] ++ nodes(),
    Items = [{wxTreeCtrl:appendItem(List, Root ,atom_to_list(Node)
            ++get_number_of_values_on_node(Node))
             ,node_status(Node)}
            || Node <- All_Nodes],
    % Iterate the list of nodes and add info if they are 
    % running a database instance
    %
    [append_statuslist(List, ItemId, StatusList) 
    || {ItemId, StatusList} <- Items],
    wxTreeCtrl:expand(List,Root).


append_statuslist(Tree, ItemId, StatusList) ->
    [wxTreeCtrl:appendItem(Tree, ItemId, Status)
    || Status <- StatusList].


node_status(Node) ->
    case process_exists(?NETWORK,Node) of
        true  -> format_text(call(?NETWORK,Node));
        false -> []
    end
    ++ % Append status from backend
    case process_exists(?BACKEND,Node) of
        true  -> format_text(call(?BACKEND,Node));
        false -> []
    end.

get_number_of_values_on_node(Node) ->
    case process_exists(?BACKEND, Node) of
        true  -> {_,[Size]} = call(?BACKEND, Node),
                 "  (" ++ integer_to_list(Size) ++")";
        false -> ""
    end.

% Returns a list of strings (separated by newline)
format_text({Format, Params}) ->
    Text = io_lib:format(Format, Params),
    String = lists:flatten(Text),
    string:tokens(String, "\n").


call(Name,Node) ->
    gen_server:call({Name,Node}, {status}).

process_exists(Name, Node) ->
    case rpc:call(Node, erlang, whereis, [Name]) of
        undefined -> false;
        _         -> true
    end.

% Local help function to parse node names from selected
% items in tree
parse_tree_selection(Tree) ->
    {_,Selected} = wxTreeCtrl:getSelections(Tree),
    Selected_Text = [wxTreeCtrl:getItemText(Tree,Sel) || Sel <- Selected],
    lists:map(fun(Node) -> [Nod|_Rest] = string:tokens(Node, " "),
                           list_to_atom(Nod)
                           end, Selected_Text).

-endif. %% windowed

